package com.semidata.trp.decision.filter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.semidata.rtd.core.iplib.Area;
import com.semidata.rtd.core.log.Logger;
import com.semidata.rtd.core.service.ServiceManager;
import com.semidata.rtd.engine.job.Job;
import com.semidata.rtd.engine.job.SyncTask;
import com.semidata.trp.decision.bid.CompileService;
import com.semidata.trp.decision.bid.DynamicBidTask;
import com.semidata.trp.decision.business.BudgetData;
import com.semidata.trp.decision.business.BusinessData;
import com.semidata.trp.decision.business.Material;
import com.semidata.trp.decision.business.Position;
import com.semidata.trp.decision.business.Resource;
import com.semidata.trp.decision.business.Strategy;
import com.semidata.trp.decision.config.Config;
import com.semidata.trp.decision.job.DecisionJob;
import com.semidata.trp.decision.job.DecisionJobContext;
import com.semidata.trp.decision.request.Request;
import com.semidata.trp.decision.utils.Utils;
import com.semidata.trp.message.business.Business;

public class TaskFilter extends SyncTask {

	private Area area;
	private int hour;
	private BusinessData businessData;
	private BudgetData budgetData;
	private DecisionJobContext context;
	private Position position;
	private Resource resource;
	private Resource defaultResource;
	private List<DynamicBidTask> dynamicTask = new ArrayList<>();
	public TaskFilter(Job job, long timeout) {
		super(job, timeout);
	}

	@Override
	public void run() {
		try {
			DecisionJob job = ((DecisionJob) getJob());
			context = job.getJobContext();
			businessData = context.getBusinessData();
			budgetData = context.getBudgetData();
			Request request = context.getRequest();

			int positionId = request.getPositionId();
			position = businessData.getPosition(positionId);
			if (position == null)
				throw new RuntimeException("no position id = " + positionId);

			area = request.getArea();
			hour = request.getHour();

			if (position.getResources().isEmpty())
				throw new RuntimeException("no resource in position "
				        + position.getId());
			// resource = position.getResources().get(0);
			for (Resource r : position.getResources()) {
				if (this.filterResource(r)) {
					if (r.getAreas().isEmpty() && r.getHours().isEmpty()) {
						defaultResource = r;
						Logger.log("DEBUG", "default resource is : "
						        + defaultResource.getId());
					} else {
						resource = r;
						Logger.log("DEBUG",
						        "resource be select : " + resource.getId()
						                + " areas size : "
						                + resource.getAreas().size()
						                + " hours size : "
						                + resource.getHours().size());
						break;
					}
				} else {
					Logger.log("DEBUG", "resource be filter : " + r.getId());
				}
			}
			if (resource == null) {
				resource = defaultResource;
				Logger.log("DEBUG",
				        "select default resource as current resource");
			}
			context.setPosition(position);
			context.setResource(resource);

			// int min = context.getPosition().getMin();
			int max = context.getPosition().getMax();

			// Set<Material> materials = new HashSet<>();

			// FilterGroup group = null;

			int strategyPriority = Integer.MIN_VALUE;
			List<FilterResult> results = new ArrayList<>();

			Logger.log("DEBUG", "start filter...");
			for (Strategy strategy : resource.getStrategies()) {
				if (strategy.getPriority() < strategyPriority && results.size() != 0) {
					break;
				} else if (strategy.getPriority() > strategyPriority) {
					results = new ArrayList<>();
				}
				strategyPriority = strategy.getPriority();
				Logger.log("DEBUG",
				        "Filter: handle strategy " + strategy.getId());
				if (!filter(strategy, resource)) {
					Logger.log("DEBUG", "Filter: strategy " + strategy.getId()
					        + " is filtered in areas or hour");
					continue;
				}

				if (!filterKeyword(strategy, request)) {
					Logger.log("DEBUG", "Filter: strategy " + strategy.getId()
					        + " is filtered in keyword");
					continue;
				}

				Map<Material, Integer> validMaterials = filterMaterial(
				        strategy, context.getFrequency());
				if (validMaterials.size() < max) {
					Logger.log("DEBUG", "valid material size is : " + validMaterials.size());
					continue;
				}
				
				FilterResult result = new FilterResult(strategy);
				result.setMaterials(validMaterials);
				results.add(result);
			}
			if (results.isEmpty())
				throw new RuntimeException();
			context.setFilterResults(results);
			Map<Integer,Class<?>> algos = ServiceManager.getService(CompileService.class).getAlgos();
			for (int i = 0; i<results.size(); i++) {
				FilterResult result = results.get(i);
				Strategy strategy = result.getStrategy();
				if (strategy.getPriority() < 0 || strategy.getPrice() == 0.0)
					break;
				if (strategy.getAlgoId() == 0)
					continue;
				
				if (!algos.containsKey(strategy.getAlgoId())) {
					continue;
				}
				DynamicBidTask task = new DynamicBidTask(job,i,result,Config.getConfig().getBidAlgorithmTimeout());
				this.dynamicTask.add(task);
			}
			if (this.dynamicTask.size() > 0) {
				context.setHasDynamicBid(true);
			}

		} catch (Exception e) {
			Logger.log("DEBUG", "error occurs when filtering");
			Logger.log("ERROR", e);
			throw new RuntimeException(e);
		}
	}
	public List<DynamicBidTask> getTasks() {
		return this.dynamicTask;
	}
	public Map<Material, Integer> filterMaterial(Strategy strategy,
	        Map<String, Integer> frequencies) {
		// Integer strategyFrequency = strategy.getFrequency();
		//
		// if (strategyFrequency == null)
		// strategyFrequency = 0;

		Map<Material, Integer> materials = new HashMap<>();

		if (frequencies.size() != 0) {
			for (Entry<Material, Integer> entry : strategy.getMaterialWeights()
			        .entrySet()) {
				
				Material material = entry.getKey();
				Integer weight = entry.getValue();
				
				Integer id = material.getId();

				int userFrequency = 0;
				if (frequencies.containsKey(id.toString()))
					userFrequency = frequencies.get(id.toString());

				Integer materialFrequency = material.getFrequency();
				if (materialFrequency == null)
					materialFrequency = 0;

				if (userFrequency < materialFrequency || materialFrequency == 0)
					materials.put(material, weight);
			}
		} else {
			for (Entry<Material, Integer> entry : strategy.getMaterialWeights()
			        .entrySet()) {
				materials.put(entry.getKey(), entry.getValue());
			}
		}
		return materials;
	}

	private boolean filter(Strategy strategy, Resource resource) {
		boolean isInArea = Utils.areaInSet(area, strategy.getAreas());
		boolean isInHour = Utils.hourInSet(hour, strategy.getHours());
		boolean isOrgActive = budgetData.isOrgActive(strategy.getOrgId(),
		        position.getMediaType(), resource);
		boolean isStrategyActive = budgetData.isStrategyActive(
		        strategy.getId(), position.getMediaType());
		;
		Logger.log("DEBUG", "fileter function result : area " + isInArea);
		Logger.log("DEBUG", "fileter function result : hour" + isInHour);
		Logger.log("DEBUG", "fileter function result : org " + isOrgActive);
		Logger.log("DEBUG", "fileter function result : strategy "
		        + isStrategyActive);
		return isInArea && isInHour && isOrgActive && isStrategyActive;
	}

	private boolean filterResource(Resource resource) {
		boolean areaFlag = Utils.areaInSet(area, resource.getAreas());
		boolean hourFlag = Utils.hourInSet(
		        hour, resource.getHours());
		Logger.log("DEBUG", "filter resource area : " + areaFlag + " resource id : " + resource.getId());
		Logger.log("DEBUG", "filter resource hour : " + hourFlag + " resource id : " + resource.getId());
		return areaFlag && hourFlag;
	}

	private boolean filterKeyword(Strategy strategy, Request request) {
		Logger.log("DEBUG", "strategy keywords : "
		        + strategy.getKeywords().size());
		boolean noHasRequestKeyword = (request.getKeyword1() == null
		        && request.getKeyword2() == null && request.getKeyword3() == null);
		boolean noHasStrategyKeyword = (strategy.getKeywords() == null || strategy
		        .getKeywords().size() == 0);
		if (noHasRequestKeyword)
			return true;
		if (!noHasRequestKeyword && noHasStrategyKeyword) {
			if (strategy.getPriority() < 0)
				return true;
			else
				return false;
		}

		String keyword1 = request.getKeyword1();
		String keyword2 = request.getKeyword2();
		String keyword3 = request.getKeyword3();
		String key1, key2, key3;
		for (Business.KeyWord kw : strategy.getKeywords()) {
			key1 = kw.getKeyword1();
			key2 = kw.getKeyword2();
			key3 = kw.getKeyword3();
			if (keyword1 == null)
				return true;
			if (key1.equals(keyword1)) {
				if (key2.isEmpty())
					return true;
				if (keyword2 != null && keyword2.equals(key2)) {
					if (key3.isEmpty())
						return true;
					if (keyword3 != null && keyword3.equals(key3))
						return true;
				}
			}

		}
		return false;
	}

}
